/**
 * 
 */
package com.oron3.bouncer.objects.shapes;





/**
 * @author Ofek
 *
 */
public class Rectangle implements Shape {
	private final LineSegment l1;
	private final LineSegment l2;
	public final Plane p;
	private final LineSegment l3;
	private final LineSegment l4;
	/**
	 * NOT Touching the plane this rectangle is seating on, is inside rectangle range, 
	 * the direction outside the plane to the object is heading normal direction
	 */
	public static final int RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_NORAML_DIRECTION = 0;
	/**
	 * NOT Touching the plane this rectangle is seating on, is inside rectangle range, 
	 * the direction outside the plane to the object is heading opposite normal direction
	 */
	public static final int RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_OPPOSITE_NORAML_DIRECTION=1;
	/**
	 * NOT Touching the plane this rectangle is seating on, is outside rectangle range, 
	 * the direction outside the plane to the object is heading normal direction
	 */
	public static final int RELATION_NOT_INTERSECT_OUTSIDE_BOUNDS_IN_NORAML_DIRECTION=2;
	/**
	 * NOT Touching the plane this rectangle is seating on, is outside rectangle range, 
	 * the direction outside the plane to the object is heading opposite normal direction
	 */
	public static final int RELATION_NOT_INTERSECT_OUTSIDE_BOUNDS_IN_OPPOSITE_NORAML_DIRECTION=3;
	/**
	 * Touching the plane this rectangle is seating on and is inside rectangle range
	 */
	public static final int RELATION_INTERSECT_INSIDE_BOUNDS = 4;
	/**
	 * Touching the plane this rectangle is seating on but is outside rectangle range
	 */
	public static final int RELATION_INTERSECT_OUTSIDE_BOUNDS = 5;
	public static final int RELATION_INTERSECT_PART_OUTSIDE_BOUNDS_PART_INSIDE_BOUNDS = 6;


	/**
	 * NOTE :  
	 * @param p1 
	 * @param p2 
	 * @param p3
	 */
	public Rectangle(final Point p1, final Point p2, final Point p3) {
		p=new Plane(p1,p2,p3);
		l1 = new LineSegment(p1, p2);
		l2 = new LineSegment(p3, p2);
		final Point p2reflection = p1.add(p3.sub(p2));
		l3 = new LineSegment(p1, p2reflection);
		l4 = new LineSegment(p3, p2reflection);
	}
	@Override
	public Shape project(final Shape p) {
		if ( p instanceof Point) {
			final Point project = (Point)this.p.project(p);
			if (l1.project(p)==null || l2.project(p)==null) return null;
			return project;
		} throw new IllegalArgumentException();

	}


	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.GLObject#intersects(com.oron3.bouncer.objects.GLObject)
	 */
	@Override
	public boolean intersects(final Shape o) {
		if ( o instanceof Sphere ) {
			final Sphere s = (Sphere) o;
			final Point projection = (Point) project(s.center);
			if (projection==null) return false;
			return new Vector(s.center.sub(projection)).length()<=s.r;
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Shape#intersection(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public Intersection intersection(final Shape o) {
		if ( o instanceof Line ) {

			final Line l = (Line) o;
			if (isInside(l.p1) && isInside(l.p1.add(l.d))) {
				return getIntersectionLineSegment(l);
			}
			final Vector vDirection = l.d;
			final Point p = l.p1;

			final Point project = (Point) project(p);
			if ( project==null ) return null;
			final Vector u = new Vector(project.sub(p));
			final Vector uDirection = u.normalize();
			final float cosa = vDirection.dot(uDirection);

			float vLength;
			if ( cosa==0 ) {
				final Point add = p.add(vDirection.mul(u.length()));
				if ( !isInside(add) ) return null;
				return new Intersection(add);
			}
			if ( cosa<0 ) {
				vLength = u.length()/(-cosa);
				final Point add = p.add(vDirection.mul(-vLength));
				if ( !isInside(add) ) return null;
				return new Intersection(add);
			}
			else {
				vLength = u.length()/(cosa);
				final Point add = p.add(vDirection.mul(vLength));
				if ( !isInside(add) ) return null;
				return new Intersection(add);
			}
		} else if ( o instanceof LineSegment ) {

			final LineSegment l = (LineSegment) o;
			if (isInside(l.line.p1) && isInside(l.line.p1.add(l.line.d))) {
				return getIntersectionLineSegment(l);
			}
			final Vector vDirection = l.line.d;
			final Point p = l.line.p1;

			final Point project = (Point) project(p);
			if ( project==null ) return null;
			final Vector u = new Vector(project.sub(p));
			final Vector uDirection = u.normalize();
			final float cosa = vDirection.dot(uDirection);

			final float vLength;
			if ( cosa==0 ) {
				final Point add = p.add(vDirection.mul(u.length()));
				if ( !isInside(add) ||  !l.isInside(add)) return null;
				return new Intersection(add);
			}
			if ( cosa<0 ) {
				vLength = u.length()/(-cosa);
				final Point add = p.add(vDirection.mul(-vLength));
				if ( !isInside(add) ||  !l.isInside(add) ) return null;
				return new Intersection(add);
			}
			else {
				vLength = u.length()/(cosa);
				final Point add = p.add(vDirection.mul(vLength));
				if ( !isInside(add)  ||  !l.isInside(add)) return null;
				return new Intersection(add);
			}
		}

		return null;
	}
	/**
	 * @param l 
	 * @return
	 */
	private Intersection getIntersectionLineSegment(final Shape l) {
		throw new IllegalArgumentException();
		//		final Intersection intersectionl1 = l1.intersection(l);
		//		final Intersection intersectionl2 = l2.intersection(l);
		//		final Intersection intersectionl3 = l3.intersection(l);
		//		final Intersection intersectionl4 = l4.intersection(l);
		//		return null;
	}
	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Plane#calcRelation(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public int describeRelation(final Shape o) {
		final int planeRelation = p.describeRelation(o);
		if ( o instanceof Point ) {
			if ( planeRelation == Plane.RELATION_IN_BOTH_DIRECTION) 
				throw new IllegalArgumentException();
			final Point p = (Point) o;
			final boolean inside = l1.project(p)!=null && l2.project(p)!=null;
			if ( planeRelation!=Plane.RELATION_OVERLAP ) {
				if ( inside ) {
					if ( planeRelation==Plane.RELATION_IN_NORAML_DIRECTION) return RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_NORAML_DIRECTION;
					if ( planeRelation==Plane.RELATION_IN_OPPOSITE_NORAML_DIRECTION) return RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_OPPOSITE_NORAML_DIRECTION;
				} else {
					if ( planeRelation==Plane.RELATION_IN_NORAML_DIRECTION) return RELATION_NOT_INTERSECT_OUTSIDE_BOUNDS_IN_NORAML_DIRECTION;
					if ( planeRelation==Plane.RELATION_IN_OPPOSITE_NORAML_DIRECTION) return RELATION_NOT_INTERSECT_OUTSIDE_BOUNDS_IN_OPPOSITE_NORAML_DIRECTION;
				}

			} else {
				if ( inside ) return RELATION_INTERSECT_INSIDE_BOUNDS;
				return RELATION_INTERSECT_OUTSIDE_BOUNDS;
			}
		} 
		//		else if ( o instanceof Sphere ) {
		//			final Sphere s = (Sphere) o;
		//			final Vector direction1 = l1.line.d.normalize();
		//			final Vector direction2 = l2.line.d.normalize();
		//			final Vector direction3 = direction1.cross(direction2);
		//			final Vector direction1step = direction1.mul(s.r);
		//			final Vector direction2step = direction2.mul(s.r);
		//			final Vector direction3step = direction3.mul(s.r);
		//			final Point point11 = s.center.add(direction1step);
		//			final Point point12 = s.center.sub(direction1step);
		//			final Point point21 = s.center.add(direction2step);
		//			final Point point22 = s.center.sub(direction2step);
		//			final Point point31 = s.center.add(direction3step);
		//			final Point point32 = s.center.sub(direction3step);
		//			final int point11relation = describeRelation(point11);
		//			final int point12relation = describeRelation(point12);
		//			final int point21relation = describeRelation(point21);
		//			final int point22relation = describeRelation(point22);
		//			final int point31relation = describeRelation(point31);
		//			final int point32relation = describeRelation(point32);
		//			if ( point11relation == RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_NORAML_DIRECTION &&
		//					point12relation == RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_NORAML_DIRECTION &&
		//					point21relation == RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_NORAML_DIRECTION &&
		//					point22relation == RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_NORAML_DIRECTION &&
		//					point31relation == RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_NORAML_DIRECTION &&
		//					point32relation == RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_NORAML_DIRECTION) 
		//				return RELATION_NOT_INTERSECT_INSIDE_BOUNDS_IN_NORAML_DIRECTION;
		//		}
		throw new IllegalArgumentException();
	}
	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Shape#isInside(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public boolean isInside(final Shape o) {
		if ( o instanceof Point ) return describeRelation(o)==RELATION_INTERSECT_INSIDE_BOUNDS;
		throw new IllegalArgumentException();
	}
	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Shape#collision(com.oron3.bouncer.objects.shapes.Shape, com.oron3.bouncer.objects.shapes.Vector)
	 */
	@Override
	public Collision collision(final Shape shape, final Vector v) {
		if ( shape instanceof Shape) {
			final Sphere s = (Sphere) shape;
			final Point project = (Point) p.project(s.center);
			final Vector step = l1.line.d.mul(s.r);
			final LineSegment extendedLine = new LineSegment(l1.line.p1.sub(step),l1.p2.add(step));
			final Intersection intersection = extendedLine.intersection(new Line(s.center,s.center.add(v)));
			if (intersection==null) return null;
			throw new IllegalArgumentException();
		}
		throw new IllegalArgumentException();
	}



}
